<template>
  <ChunkFileUploader
    ref="uploader"
    :auto-start="false"
    :options="options"
    :file-status-text="{
      success: '上传成功',
      error: '上传失败',
      uploading: '上传中',
      paused: '暂停',
      waiting: '等待'
    }"
    class="fast-file-uploader"
    @file-added="onFileAdded"
    @file-error="onFileError"
    @file-success="onFileSuccess">
    <ChunkFileUploaderUnSupport />
    <ChunkFileUploaderButton ref="uploadBtn" :attrs="attrs" class="fast-file-uploader-upload-button">
      选择文件
    </ChunkFileUploaderButton>

    <ChunkFileUploaderFiles v-if="useFiles" v-show="panelShow">
      <template #default="{ files }">
        <div :class="{ collapse: collapse }" class="file-panel">
          <div class="file-title">
            <h3>文件列表</h3>
            <div class="operate flex-item">
              <ElButton :title="collapse ? '折叠' : '展开'" type="text" @click="fileListShow">
                <i :class="collapse ? 'el-icon-arrow-down' : 'el-icon-arrow-up'"></i>
              </ElButton>
              <ElButton title="关闭" type="text" @click="close">
                <i class="el-icon-close"></i>
              </ElButton>
            </div>
          </div>
          <ul v-show="collapse" class="file-list">
            <li v-for="file in files" :key="file.id">
              <ChunkFileUploaderFile :file="file" />
            </li>
            <li v-if="!files.length" class="no-file"><i class="iconfont icon-empty-file"></i> 暂无待上传文件</li>
          </ul>
        </div>
      </template>
    </ChunkFileUploaderFiles>

    <ChunkFileUploaderList v-else v-show="panelShow">
      <template #default="{ fileList }">
        <div :class="{ collapse: collapse }" class="file-panel">
          <div class="file-title">
            <h3>文件列表</h3>
            <div class="operate flex-item">
              <ElButton :title="collapse ? '折叠' : '展开'" type="text" @click="fileListShow">
                <i :class="collapse ? 'el-icon-arrow-down' : 'el-icon-arrow-up'"></i>
              </ElButton>
              <ElButton title="关闭" type="text" @click="close">
                <i class="el-icon-close"></i>
              </ElButton>
            </div>
          </div>
          <ul v-show="collapse" class="file-list">
            <li v-for="file in fileList" :key="file.id">
              <ChunkFileUploaderFile :file="file" />
            </li>
            <li v-if="!fileList.length" class="no-file"><i class="iconfont icon-empty-file"></i> 暂无待上传文件</li>
          </ul>
        </div>
      </template>
    </ChunkFileUploaderList>
  </ChunkFileUploader>
</template>

<script>
  import { chunk } from '@/api/attachment/attachment'
  import uploader from '@/components/chunk-file-uploader'
  import { computeFileHash } from '@/components/uploader/js/hash'
  import { defaultHeaders } from '@/http'
  import { isPromise } from '@web-utils/core'

  const {
    ChunkFileUploader,
    ChunkFileUploaderButton,
    ChunkFileUploaderUnSupport,
    ChunkFileUploaderList,
    ChunkFileUploaderFiles,
    ChunkFileUploaderFile
  } = uploader

  export default {
    name: 'FastFileUploader',
    components: {
      ChunkFileUploader,
      ChunkFileUploaderButton,
      ChunkFileUploaderUnSupport,
      ChunkFileUploaderList,
      ChunkFileUploaderFiles,
      ChunkFileUploaderFile
    },
    props: {
      params: {
        type: Object,
        default: () => ({})
      },
      fileAdd: Function
    },
    data() {
      return {
        uploadIds: {},
        options: {
          target: chunk(),
          chunkSize: 5 * 1024 * 1024,
          maxChunkRetries: 3,
          withCredentials: false,
          testChunks: true,
          // 服务器分片校验函数，秒传及断点续传基础
          checkChunkUploadedByResponse: (chunk, message) => {
            let checkResult = {}
            try {
              checkResult = JSON.parse(message).data
              this.uploadIds[checkResult.identifier] = checkResult
            } catch (e) {
              console.error(e)
            }
            if (checkResult.fileUploaded) {
              return true
            }
            return (checkResult.uploadedChunks || []).indexOf(`${chunk.offset + 1}`) >= 0
          },
          processParams: params => {
            const mergedParams = JSON.parse(
              JSON.stringify({
                ...params,
                ...this.params,
                key: this.uploadIds[params.identifier]?.key,
                uploadId: this.uploadIds[params.identifier]?.uploadId
              })
            )
            return mergedParams
          },
          allowDuplicateUploads: true,
          headers: defaultHeaders()
        },
        attrs: {
          accept: '*/*'
        },
        panelShow: false,
        collapse: false,
        useFiles: true
      }
    },
    computed: {
      // Uploader实例
      uploader() {
        return this.$refs.uploader.uploader
      },
      limitSize() {
        return 3000
      }
    },
    methods: {
      openUploader() {
        this.$refs.uploadBtn?.$refs.btn?.click()
      },
      async onFileAdded(file) {
        file.pause()
        this.collapse = true
        if (file.size / (1024 * 1024) > this.limitSize) {
          this.$message.error(`上传文件不能超过${this.limitSize}M!`)
          return
        }
        if (this.fileAdd) {
          if (isPromise(this.fileAdd)) {
            await this.fileAdd()
          } else {
            const res = this.fileAdd()
            if (!res) {
              return
            }
          }
        }
        this.panelShow = true
        await this.computeIdentifier(file)
        file.resume()
        this.$emit('file-added')
      },
      async onFileSuccess(rootFile, file, response, chunk) {
        delete this.uploadIds[file.uniqueIdentifier]
        const res = JSON.parse(response)
        this.$emit('success', res.data)
      },
      onFileError() {
        this.$message.error('文件上传失败')
      },
      /**
       * 计算文件唯一标识，实现断点续传及秒传
       * @param file
       */
      async computeIdentifier(file) {
        this.$set(file, 'hashing', true)
        const hash = await computeFileHash(file.file, {
          onUpdate: (currentChunk, totalChunk) => {
            this.$set(file, 'hashProgress', `校验中 ${((currentChunk / totalChunk) * 100).toFixed(0)}%`)
          },
          onError: () => {
            this.error(`文件${file.name}读取出错，请检查该文件`)
            file.cancel()
          }
        })
        this.$set(file, 'hashing', false)
        file.uniqueIdentifier = hash
      },
      fileListShow() {
        this.collapse = !this.collapse
      },
      close() {
        this.uploader.cancel()
        this.panelShow = false
      },
      error(msg) {
        this.$notify.error({ title: '错误', message: msg, duration: 2000 })
      }
    }
  }
</script>

<style lang="scss">
  .fast-file-uploader {
    position: absolute;
    right: 15px;
    bottom: 15px;
    z-index: 20;
    width: 520px;

    .file-panel {
      background-color: #fff;
      border: 1px solid #e2e2e2;
      border-radius: 7px 7px 0 0;
      box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);

      .file-title {
        display: flex;
        height: 40px;
        padding: 0 15px;
        line-height: 40px;
        border-bottom: 1px solid #ddd;

        .operate {
          text-align: right;
        }
      }

      .file-list {
        position: relative;
        height: 240px;
        overflow: hidden auto;
        background-color: #fff;

        > li {
          background-color: #fff;
        }
      }

      &.collapse {
        .file-title {
          background-color: #e7ecf2;
        }
      }
    }

    .no-file {
      position: absolute;
      top: 50%;
      left: 50%;
      font-size: 16px;
      transform: translate(-50%, -50%);
    }

    .uploader-file-icon {
      &::before {
        content: '' !important;
      }

      &[icon='image'] {
        background: url('./images/image-icon.png');
      }

      &[icon='video'] {
        background: url('./images/video-icon.png');
      }

      &[icon='document'] {
        background: url('./images/text-icon.png');
      }
    }

    .uploader-file-actions > span {
      margin-right: 6px;
    }

    .fast-file-uploader-upload-button {
      // position: absolute;
      // clip: rect(0, 0, 0, 0);
      display: none;
    }
  }
</style>
